package br.com.lambdacore.lang;

import static br.com.lambdacore.validator.Validator.is;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import br.com.lambdacore.lang.annotation.EqualsHashCode;
import br.com.lambdacore.lang.reflect.AnnotationUtils;
import br.com.lambdacore.util.MapUtil;

public class EqualsBuilder extends
		org.apache.commons.lang.builder.EqualsBuilder {

	private Map<Field, Object> equalsFields = MapUtil.newHashMap();
	private Map<Method, Object> equalsMethods = MapUtil.newHashMap();

	private Object thiz;

	@SuppressWarnings("unchecked")
	public EqualsBuilder(Object thiz) {
		this.setThiz(thiz);
		try {
			Collection<Field> fields = AnnotationUtils.getAllFieldsAnnoted(thiz
					.getClass(), EqualsHashCode.class);
			for (Field field : fields) {
				field.setAccessible(true);
				this.equalsFields.put(field, field.get(this.getThiz()));
			}
			Collection<Method> methods = AnnotationUtils.getAllMethodsAnnoted(
					thiz.getClass(), EqualsHashCode.class);
			for (Method method : methods) {
				method.setAccessible(true);
				this.equalsMethods.put(method, method.invoke(this.getThiz()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public boolean equals(Object other) {
		if (is(other).nil()) {
			return false;
		}
		if (other == this) {
			return true;
		}
		if (is(this.getThiz().getClass()).notInstanceOf(other.getClass())) {
			return false;
		}
		try {
			Set<Field> fields = this.equalsFields.keySet();
			for (Field field : fields) {
				this.append(this.equalsFields.get(field), field.get(other));
			}
			for (Entry<Method, Object> entry : this.equalsMethods.entrySet()) {
				this.append(entry.getValue(), entry.getKey().invoke(other));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this.isEquals();
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}

	private Object getThiz() {
		return this.thiz;
	}

	private void setThiz(Object thiz) {
		this.thiz = thiz;
	}

	public static EqualsBuilder newInstance(Object thiz) {
		return new EqualsBuilder(thiz);
	}

}
